<?php
// tunnel.php - 全协议隧道代理
// 使用方式：上传到PHP服务器，通过访问此文件代理到本地127.0.0.1:3001

set_time_limit(0);
ignore_user_abort(true);
header('X-Tunnel-Proxy: PHP/1.0');

// ===== 配置部分 =====
$local_host = '127.0.0.1';  // 本地服务地址
$local_port = 3000;         // 本地服务端口
$enable_debug = true;       // 是否开启调试日志
$allowed_ips = [];          // IP白名单，空数组允许所有IP
// ===================

// 安全检查
if (!empty($allowed_ips) && !in_array($_SERVER['REMOTE_ADDR'], $allowed_ips)) {
    header("HTTP/1.1 403 Forbidden");
    die("Access denied for your IP: ".$_SERVER['REMOTE_ADDR']);
}

// 调试日志
function log_debug($message) {
    global $enable_debug;
    if ($enable_debug) {
        file_put_contents('tunnel_debug.log', date('[Y-m-d H:i:s] ') . $message . "\n", FILE_APPEND);
    }
}

// 获取原始请求数据
$raw_data = file_get_contents('php://input');
$headers = getallheaders();

log_debug("New connection from: ".$_SERVER['REMOTE_ADDR']);
log_debug("Request method: ".$_SERVER['REQUEST_METHOD']);
log_debug("Request URI: ".$_SERVER['REQUEST_URI']);

// 建立到本地服务的连接
$local_socket = @fsockopen($local_host, $local_port, $errno, $errstr, 10);
if (!$local_socket) {
    header("HTTP/1.1 502 Bad Gateway");
    die("Failed to connect to local service: $errstr ($errno)");
}

stream_set_timeout($local_socket, 5);
stream_set_blocking($local_socket, false);

// ===== HTTP 请求处理 =====
if (isset($_SERVER['REQUEST_METHOD'])) {
    $request_method = $_SERVER['REQUEST_METHOD'];
    $request_path = parse_url($_SERVER['REQUEST_URI'], PHP_URL_PATH);
    $query_string = isset($_SERVER['QUERY_STRING']) && $_SERVER['QUERY_STRING'] ? '?'.$_SERVER['QUERY_STRING'] : '';
    
    // 构造HTTP请求行
    $http_request = "$request_method $request_path$query_string HTTP/1.1\r\n";
    
    // 添加必要头信息
    $http_request .= "Host: $local_host:$local_port\r\n";
    $http_request .= "X-Forwarded-For: ".$_SERVER['REMOTE_ADDR']."\r\n";
    $http_request .= "X-Forwarded-Proto: ".(@$_SERVER['HTTPS'] ? 'https' : 'http')."\r\n";
    
    // 转发其他头（排除PHP自动添加的头）
    $skip_headers = ['host', 'connection', 'content-length', 'accept-encoding'];
    foreach ($headers as $name => $value) {
        if (!in_array(strtolower($name), $skip_headers)) {
            $http_request .= "$name: $value\r\n";
        }
    }
    
    // 如果存在请求体，添加Content-Length
    if (!empty($raw_data)) {
        $http_request .= "Content-Length: ".strlen($raw_data)."\r\n";
    }
    
    $http_request .= "\r\n";  // 头结束空行
    
    log_debug("Forwarding HTTP request:\n".$http_request);
    
    // 发送HTTP请求
    fwrite($local_socket, $http_request);
    
    // 发送请求体（如果有）
    if (!empty($raw_data)) {
        fwrite($local_socket, $raw_data);
    }
} else {
    // 非HTTP请求直接转发原始数据
    if (!empty($raw_data)) {
        fwrite($local_socket, $raw_data);
    }
}

// ===== 双向数据转发 =====
$read = [$local_socket];
$write = null;
$except = null;
$response_sent = false;

while (true) {
    $changed = @stream_select($read, $write, $except, 5);
    
    if ($changed === false) {
        log_debug("Stream select error");
        break;
    }
    
    if ($changed > 0) {
        foreach ($read as $stream) {
            $data = fread($stream, 8192);
            
            if ($data === false || $data === '') {
                log_debug("Connection closed by remote");
                break 2;
            }
            
            if (!$response_sent && isset($_SERVER['REQUEST_METHOD'])) {
                // 对于HTTP请求，我们需要解析第一个响应包
                $response_lines = explode("\r\n", $data);
                $first_line = array_shift($response_lines);
                
                if (preg_match('/^HTTP\/\d\.\d (\d{3})/', $first_line, $matches)) {
                    $response_code = $matches[1];
                    log_debug("Received HTTP response: $first_line");
                }
                
                $response_sent = true;
            }
            
            echo $data;
            ob_flush();
            flush();
        }
    }
    
    // 检查是否有新的输入数据
    $new_data = file_get_contents('php://input');
    if ($new_data && $new_data != $raw_data) {
        fwrite($local_socket, $new_data);
        $raw_data = $new_data;
    }
    
    $read = [$local_socket];
}

// 关闭连接
fclose($local_socket);
log_debug("Connection closed");
?>